home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-30 | 11.2 KB | 417 lines | [TEXT/KAHL] |
- /******************************************************************************
- CDecimalText.c
-
- A subclass of CDialogText that accepts only decimal entries. Any
- characters are accepted as the user types, and the entry is checked
- at validation. Validation occurs when the user tabs out of the field
- or tries to confirm the dialog.
-
- CDecimalText can constrain values to a certain number of decimal
- places. If 0 decimal places are allowed, then users may enter
- only integers. If more than 0 decimal places are allowed, then
- users may enter numbers of only so many decimal places. To allow
- entry of any number, allow 50 decimal places, for example.
-
- SANE is used to to convert the text to a number because it provides
- Script Manager support.
-
- As a subclass of CDialogText, CDecimalText fields may or not be required
- and have a maximum allowed length. Required fields are considered invalid
- if left empty. Additionally CDecimalText fields may have a default value,
- which is the value returned if the text is empty. They also have a
- minimum and maximum allowed value.
-
- To use this class, you must include the SANE library in your project.
-
- SUPERCLASS = CDialogText
-
- Copyright © 1993 Michael Abramowicz. All rights reserved.
-
-
- ******************************************************************************/
-
- #include "CDecimalText.h"
- #include <SANE.h>
- #include "Global.h"
- #include "TBUtilities.h"
- #include <Packages.h>
-
- /******************************************************************************
- IDecimalText
-
- Initialize the CDecimalText object. The default constraints are:
- no max length, required, range (MINLONG, MAXLONG), showRangeOnErr
- is FALSE, and fields need not be constrained to integer values.
- ******************************************************************************/
-
- void CDecimalText::IDecimalText( CView *anEnclosure, CView *aSupervisor,
- short aWidth, short aHeight,
- short aHEncl, short aVEncl,
- SizingOption aHSizing, SizingOption aVSizing,
- short aLineWidth)
- {
- CDialogText::IDialogText( anEnclosure, aSupervisor, aWidth, aHeight,
- aHEncl, aVEncl, aHSizing, aVSizing, aLineWidth);
-
- minValue = MINLONG;
- maxValue = MAXLONG;
- defaultValue = 0;
- isRequired = TRUE;
- decimalPlaces = 50; /* Virtually no limit. */
- }
-
- /******************************************************************************
- IViewTemp
-
- Initialize the CDecimalText object from a resource template.
- ******************************************************************************/
-
- void CDecimalText::IViewTemp(CView *anEnclosure, CBureaucrat *aSupervisor,
- Ptr viewData)
- {
- tDecimalTextTempP data = (tDecimalTextTempP) viewData;
-
- inherited::IViewTemp( anEnclosure, aSupervisor, viewData);
-
- minValue = data->minValue;
- maxValue = data->maxValue;
-
- // As a special case, we interpret that
- // if minValue == maxValue, the intent is to allow all decimals
-
- if (minValue == maxValue)
- {
- minValue = MINLONG;
- maxValue = MAXLONG;
- }
-
- defaultValue = data->defaultValue;
-
- decimalPlaces = data->decimalPlaces;
- }
-
- /******************************************************************************
- SpecifyDecimalPlaces
-
- Specify how many decimal places should be allowed for the field.
- If more than the requisite number are already in this field, then
- an error is displayed if doValidate is TRUE.
- ******************************************************************************/
- void CDecimalText::SpecifyDecimalPlaces( short numPlaces, Boolean doValidate)
- {
- decimalPlaces = numPlaces;
- if (doValidate)
- if (!Validate()) {
- SelectAll(TRUE);
- BecomeGopher(TRUE);
- }
- }
-
- /******************************************************************************
- GetDecimalPlaces
-
- Return how many decimal places are allowed for the field.
- ******************************************************************************/
- short CDecimalText::GetDecimalPlaces(void)
- {
- return decimalPlaces;
- }
-
- /******************************************************************************
- SpecifyRange
-
- Specify the allowed range of values. If the range is MINLONG-MAXLONG,
- then showRangeOnErr is turned off. Note that the allowable range must
- be bound by integers.
- ******************************************************************************/
-
- void CDecimalText::SpecifyRange( long aMinimum, long aMaximum)
- {
- ASSERT( aMaximum > aMinimum);
-
- minValue = aMinimum;
- maxValue = aMaximum;
-
- }
-
- /******************************************************************************
- SpecifyDefaultValue
-
- Specifying a default value implies that the text is valid if empty,
- therefore isRequired is set FALSE. Note that the default value must
- be an integer.
- ******************************************************************************/
-
- void CDecimalText::SpecifyDefaultValue( long aDefaultValue)
- {
- defaultValue = aDefaultValue;
- isRequired = FALSE;
- }
-
- /******************************************************************************
- SetDecValue
-
- Set the text to aValue.
- ******************************************************************************/
-
- void CDecimalText::SetDecValue( double aValue)
- {
- Str255 numStr, oldText;
- extended x;
- decform dec;
- Boolean trailingZeros = FALSE;
- short index;
- long double tmp;
-
- GetTextString( oldText);
-
- tmp = aValue;
-
- #ifdef __SC__
- #if mc68881
- x96tox80(&tmp, &x);
- #else
- x = tmp;
- #endif
- #else
- #if __option(native_fp) && !__option(mc68881)
- x = tmp;
- #else
- x96tox80( &tmp, &x);
- #endif
- #endif
-
- dec.style = FIXEDDECIMAL;
- dec.digits = decimalPlaces;
-
- num2str( &dec, x, numStr);
-
- /* check for trailing 0’s */
- if (numStr[numStr[0]] == '0')
- for (index = 0; ++index < numStr[0];) {
- if (numStr[index] == '.') {
- trailingZeros = TRUE;
- index = 256;
- }
- }
-
- /* eliminate trailing 0’s if necessary */
- if (trailingZeros) {
- /* eliminate zeros */
- while ((numStr[numStr[0]] == '0') && (numStr[0] != 1))
- numStr[0]--;
- /* eliminate period from end if applicable */
- while (numStr[numStr[0]] == '.')
- numStr[0]--;
- }
-
- SetTextString( numStr);
-
- if (!Validate())
- SetTextString( oldText);
- }
-
- /******************************************************************************
- GetDecValue
-
- Get the current value. If the text is empty or the field is invalid,
- the default value is returned.
- ******************************************************************************/
-
- double CDecimalText::GetDecValue( void)
- {
- double value;
- Validity valid;
-
- ConvertToDecimal( &value, &valid);
-
- return value;
- }
-
- /******************************************************************************
- ReportInvalidText {OVERRIDE}
-
- Scroll so that the beginning of the selection is visible before the
- entire selection is selected.
- ******************************************************************************/
- void CDecimalText::ReportInvalidText( short strIndex)
- {
- SetSelection(0,0,TRUE);
- ScrollToSelection();
- inherited::ReportInvalidText(strIndex);
- }
-
- /******************************************************************************
- Validate {OVERRIDE}
-
- Ensure that all the constraints have been met. If validation fails,
- the ReportInvalidText method is called to show an alert.
- ******************************************************************************/
- Boolean CDecimalText::Validate( void)
- {
- double value;
- Validity valid;
- Str31 minText, maxText, placesText;
-
- if (inherited::Validate())
- {
- ConvertToDecimal( &value, &valid);
-
- switch (valid) {
-
- case kNonNumeric:
- ReportInvalidText( validateNonNumeric);
- break;
-
- case kNonIntegral:
- ReportInvalidText( validateNonIntegral);
- break;
-
- case kTooManyDecimalPlaces:
- NumToString( (long) decimalPlaces, placesText);
- ParamText( NULL, placesText, NULL, NULL);
- ReportInvalidText( validateDecPlaces);
- break;
-
- case kOutOfRange:
- NumToString( minValue, minText);
- NumToString( maxValue, maxText);
- ParamText( NULL, minText, maxText, NULL);
- ReportInvalidText( validateOutOfRange);
-
- default:
- break; /* Entry is valid */
- };
- return (valid == kValid);
- }
- else
- return FALSE;
- }
-
- /******************************************************************************
- ConvertToDecimal
-
- Protected method to convert the text to a double. Returns kValid if
- the text was a valid decimal entry within valid limits. If there is
- an invalid entry, then return the appropriate validity code and the
- default value.
- ******************************************************************************/
-
- void CDecimalText::ConvertToDecimal( double *decValue, Validity *valid)
- {
- Str255 str;
- short index, length;
- decimal dec;
- Boolean validPrefix = 0;
- extended x;
- double minLng, maxLng;
- double val;
-
- *valid = kValid;
-
- GetTextString( str);
-
- length = Length(str);
- if (length == 0)
- {
- if (isRequired)
- *valid = kOtherReason;
- *decValue = defaultValue;
- }
- else
- {
- index = 1;
-
-
- str2dec( str, &index, &dec, &validPrefix);
-
- // if validPrefix is TRUE and index is past the last char,
- // then the string is a valid numeric string. If the
- // exp field is non-negative, then it is a valid integer
-
- if (!(validPrefix && (index > length)))
- *valid = kNonNumeric;
- else {
- if ((decimalPlaces == 0) && (dec.exp < 0))
- *valid = kNonIntegral;
- else if ( -dec.exp > decimalPlaces)
- *valid = kTooManyDecimalPlaces;
- }
- if (*valid == kValid)
- {
- x = dec2num( &dec);
-
- #ifdef __SC__
- #if mc68881
- x80tox96( &x, &val);
- #else
- val = x;
- #endif
- #else
- #if __option(native_fp) && !__option(mc68881)
- val = x;
- #else
- x80tox96( &x, &val);
- #endif
- #endif
- minLng = (long) MINLONG;
- maxLng = MAXLONG;
- if ((val >= minLng) && (val <= maxLng) && (val >= minValue) && (val <= maxValue))
- *decValue = val;
- else
- *valid = kOutOfRange;
- }
- }
- if (*valid != kValid)
- *decValue = (double) defaultValue;
- }
-
- /******************************************************************************
- DoCommand
-
- When a cmdPaste is received, then round to the nearest permissible
- decimal, unless no decimals at all are permitted.
- ******************************************************************************/
- void CDecimalText::DoCommand(long theCommand)
- {
- CTextEditTask *editTask = NULL;
- long selStart, selEnd;
- short saveDecimalPlaces,
- tempDecimalPlaces = 50;
- double decimalValue;
- Validity valid;
-
- if (theCommand == cmdPaste) {
- GetSelection(&selStart, &selEnd);
- if (selStart == 0 && selEnd == GetLength() && decimalPlaces != 0) {
- /* Temporarily disable decimal place checking and get the
- entire decimal value. */
- saveDecimalPlaces = GetDecimalPlaces();
- SpecifyDecimalPlaces(tempDecimalPlaces, FALSE);
-
- /* Now paste in the text. */
- inherited::DoCommand(theCommand);
-
- /* Get the decimal value. */
- ConvertToDecimal(&decimalValue, &valid);
- SpecifyDecimalPlaces(saveDecimalPlaces, FALSE);
-
- /* Set to the decimal value, truncating, and scroll so
- that the beginning of the pasted in text is visible. */
- if (valid == kValid) {
- SetDecValue(decimalValue);
- GetSelection(&selStart, &selEnd);
- SetSelection(0,0, FALSE);
- ScrollToSelection();
- SetSelection(selStart, selEnd, TRUE);
- };
- }
- else { /* Handle insertion paste the regular way. */
- inherited::DoCommand(theCommand);
- }
- BroadcastChange(dialogTextChanged, NULL);
- }
- else
- inherited::DoCommand(theCommand);
-
- }